package drds.global_transaction.resource_manager.execute.dml;

import drds.common.ShouldNeverHappenException;
import drds.global_transaction.resource_manager.execute.StatementExecuteTemplate;
import drds.global_transaction.resource_manager.model.ColumnMetaData;
import drds.global_transaction.resource_manager.model.Image;
import drds.global_transaction.resource_manager.model.TableMetaData;
import drds.global_transaction.resource_manager.proxy.resource_manager.PreparedStatementProxy;
import drds.global_transaction.resource_manager.proxy.resource_manager.StatementProxy;
import drds.global_transaction.resource_manager.sql_recognizer.InsertSqlRecognizer;
import drds.global_transaction.resource_manager.sql_recognizer.SqlRecognizer;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class InsertExecutor<T, S extends Statement> extends AbstractDmlExecutor<T, S>
{

	public InsertExecutor(SqlRecognizer sqlRecognizer, StatementProxy statementProxy, StatementExecuteTemplate statementExecuteTemplate)
	{
		super(sqlRecognizer, statementProxy, statementExecuteTemplate);
	}

	@Override
	protected Image afterImage(Image beforeImage) throws SQLException
	{
		InsertSqlRecognizer insertSqlRecognizer = (InsertSqlRecognizer) sqlRecognizer;
		List<String> insertColumnNameList = insertSqlRecognizer.getInsertColumnNameList();
		TableMetaData tableMetaData = getTableMetaData();
		Image afterImage = null;
		if (tableMetaData.containsAllPrimaryKeyColumnNameList(insertColumnNameList))
		{
			// insert values including PK
			List<Object> primaryKeyObjectList = null;
			String firstPrimaryKeyColumnName = tableMetaData.getFirstPrimaryKeyColumnName();
			int size = insertColumnNameList.size();
			for (int index = 0; index < size; index++)
			{
				if (insertColumnNameList.get(index).equalsIgnoreCase(firstPrimaryKeyColumnName))
				{
					//
					if (statementProxy instanceof PreparedStatementProxy)
					{
						primaryKeyObjectList = ((PreparedStatementProxy) statementProxy).getParameterByIndex(index);
					} else
					{
						List<List<Object>> insertColumnValueList = insertSqlRecognizer.getInsertColumnValueList();
						primaryKeyObjectList = new ArrayList<>(insertColumnValueList.size());
						for (List<Object> objectList : insertColumnValueList)
						{
							primaryKeyObjectList.add(objectList.get(index));
						}
					}
					break;// end
				}
			}
			if (primaryKeyObjectList == null)
			{
				throw new IllegalArgumentException("必须含有主键值");
			}
			afterImage = getAfterImage(primaryKeyObjectList);

		} else
		{
			// PK is just auto generated
			Map<String, ColumnMetaData> primaryKeyColumnNameToColumnMetaDataHashMap = getTableMetaData().getPrimaryKeyColumnNameToColumnMetaDataHashMap();
			if (primaryKeyColumnNameToColumnMetaDataHashMap.size() != 1)
			{
				throw new IllegalStateException("只支持一个主键");
			}
			ColumnMetaData columnMetaData = primaryKeyColumnNameToColumnMetaDataHashMap.values().iterator().next();
			if (!columnMetaData.isAutoincrement())
			{
				throw new ShouldNeverHappenException();
			}
			//
			ResultSet resultSet = null;
			try
			{
				resultSet = statementProxy.getStatement().getGeneratedKeys();
			} catch (SQLException e)
			{
				// java.sql_recognizer.SQLException: Generated keys not requested. You need to
				// specify Statement.RETURN_GENERATED_KEYS to
				// Statement.executeUpdate() or Connection.prepareStatement().
				if ("S1009".equalsIgnoreCase(e.getSQLState()))
				{
					resultSet = statementProxy.getStatement().executeQuery("SELECT LAST_INSERT_ID()");
				} else
				{
					throw e;
				}
			}
			//
			List<Object> primaryKeyObjectList = new ArrayList<>();
			while (resultSet.next())
			{
				Object object = resultSet.getObject(1);
				primaryKeyObjectList.add(object);
			}
			if (primaryKeyObjectList == null)
			{
				throw new IllegalArgumentException("必须含有主键值");
			}
			afterImage = getAfterImage(primaryKeyObjectList);
		}

		if (afterImage == null)
		{
			throw new SQLException("failed to build after-image for insert");
		}

		return afterImage;
	}

	@Override
	protected Image beforeImage() throws SQLException
	{
		return Image.empty(getTableMetaData());
	}

	private Image getAfterImage(List<Object> primaryKeyObjectList) throws SQLException
	{
		Image afterImage;
		String firstPrimaryKeyColumnName = getTableMetaData().getFirstPrimaryKeyColumnName();
		StringBuilder sb = new StringBuilder("select * from " + getTableMetaData().getTableName() + " where ");
		int size = primaryKeyObjectList.size();
		for (int i = 1; i <= size; i++)
		{
			sb.append(firstPrimaryKeyColumnName + "=?");
			if (i < size)
			{
				sb.append(" or ");
			}
		}
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try
		{
			preparedStatement = statementProxy.getConnection().prepareStatement(sb.toString());
			for (int i = 1; i <= size; i++)
			{
				preparedStatement.setObject(i, primaryKeyObjectList.get(i - 1));
			}
			resultSet = preparedStatement.executeQuery();
			afterImage = Image.buildImage(getTableMetaData(), resultSet);
		} finally
		{
			if (resultSet != null)
			{
				resultSet.close();
			}
			if (preparedStatement != null)
			{
				preparedStatement.close();
			}
		}
		return afterImage;
	}
}
